package org.jsola.hr.service.impl;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.apache.commons.lang3.StringUtils;
import org.jsola.admin.dto.DictItemAddDTO;
import org.jsola.admin.dto.DictItemUpdateDTO;
import org.jsola.admin.entity.DictItemDO;
import org.jsola.core.Page;
import org.jsola.core.entity.BaseDO;
import org.jsola.exception.ParamException;
import org.jsola.hr.common.ExampleKit;
import org.jsola.hr.constant.EmpSocStatus;
import org.jsola.hr.constant.HrConstants;
import org.jsola.hr.dao.ISocProgDAO;
import org.jsola.hr.dto.*;
import org.jsola.hr.entity.*;
import org.jsola.hr.exception.HrException;
import org.jsola.hr.provider.IAdminProviderService;
import org.jsola.hr.query.SocProgQuery;
import org.jsola.hr.service.*;
import org.jsola.hr.vo.EmpSocInfoListVO;
import org.jsola.hr.vo.SocProgItemListVO;
import org.jsola.hr.vo.SocProgListVO;
import org.jsola.hr.vo.SocProgVO;
import org.jsola.log.common.LogKit;
import org.jsola.user.core.TokenUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import tk.mybatis.mapper.entity.Example;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static org.jsola.hr.constant.HrConstants.*;

/**
 * 参保方案表
 *
 * @author zhr
 */
@Service("hrSocProgServiceImpl")
public class SocProgServiceImpl implements ISocProgService {

    @Autowired
    private ISocProgDAO socProgDAO;

    @Autowired
    private ISocProgItemService socProgItemService;

    @Autowired
    private IEmpSocInfoService empSocInfoService;

    @Autowired
    private IEmpSocItemService empSocItemService;

    @Autowired
    private ICustomTemplateFieldService customTemplateFieldService;

    @Autowired
    @Lazy
    private ICompanyService companyService;

    @Autowired
    private IEmpSocPlanService empSocPlanService;

    @Autowired
    private IAdminProviderService adminProviderService;

    @Autowired
    private IEmpSocMonthService empSocMonthService;

    @Autowired
    private IEmpSocRelatedService empSocRelatedService;

    @Autowired
    @Lazy
    private IEmpSocTransformService empSocTransformService;

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public SocProgVO save(SocProgAddDTO socProgAddDTO, TokenUser tokenUser) {
        if (StringUtils.isBlank(socProgAddDTO.getProgName())) {
            throw new HrException("方案名称不可为空");
        }
        //删除不设置公积金的子项，防止不设置公积金而前端传的零
        List<SocProgItemDTO> progItemList = socProgAddDTO.getSocProgItemDTOList();
        if (socProgAddDTO.getPayProvidentFund() && !CollectionUtils.isEmpty(progItemList)) {
            progItemList.removeIf(progItem -> 2 == progItem.getItemType());
            socProgAddDTO.setSocProgItemDTOList(progItemList);
        }
        Example example = new Example(SocProgDO.class);
        example.and()
                .andEqualTo("siteId", tokenUser.getSiteId())
                .andEqualTo("companyId", socProgAddDTO.getCompanyId())
                .andEqualTo("progName", socProgAddDTO.getProgName())
                .andEqualTo("valid", true);
        int socProgCount = socProgDAO.selectCountByExample(example);
        if (socProgCount > 0) {
            throw new HrException("方案名称已存在");
        }

        // 转DO
        SocProgDO socProgDO = socProgAddDTO.to(SocProgDO.class);
        // 保存
        socProgDO = save(socProgDO, tokenUser.getSiteId(), tokenUser.getUserId());

        if (!CollectionUtils.isEmpty(progItemList)) {
            List<DictItemDO> insertList = new ArrayList<>();
            List<DictItemDO> dictItemList = adminProviderService.selectAllSocItem();
            List<String> itemNameList = dictItemList.stream().map(DictItemDO::getDictItemValue).collect(Collectors.toList());
            checkItemName(progItemList, socProgAddDTO.getCompanyId(), tokenUser);
            // 批量保存参保方案子项
            String socProgId = socProgDO.getId();
            List<SocProgItemAddDTO> socProgItemAddDTOList = progItemList.stream().map(socProgItemDTO -> {
                SocProgItemAddDTO socProgItemAddDTO = socProgItemDTO.to(SocProgItemAddDTO.class);
                socProgItemAddDTO.setProgId(socProgId);
                if (!itemNameList.contains(socProgItemAddDTO.getItemName())) {
                    DictItemDO insertItem = new DictItemDO();
                    fillDictItem(insertItem, socProgItemAddDTO);
                    insertList.add(insertItem);
                }
                return socProgItemAddDTO;
            }).collect(Collectors.toList());
            socProgItemService.batchSave(socProgItemAddDTOList, tokenUser);
            if (!CollectionUtils.isEmpty(insertList)) {
                adminProviderService.bachSave(insertList);
            }
        }
        LogKit.logForSave("创建参保方案", "SocProg", socProgAddDTO.getCompanyId(), tokenUser);
        return socProgDO.to(SocProgVO.class);
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public List<SocProgVO> batchSave(List<SocProgAddDTO> socProgAddDTOList, TokenUser tokenUser) {
        if (CollectionUtils.isEmpty(socProgAddDTOList)) {
            return null;
        }
        List<SocProgDO> socProgDOList = socProgAddDTOList
                .parallelStream()
                .map(socProgAddDTO -> {
                    SocProgDO socProgDO = socProgAddDTO.to(SocProgDO.class);
                    socProgDO.preInsert(tokenUser.getUserId());
                    socProgDO.setSiteId(tokenUser.getSiteId());
                    return socProgDO;
                }).collect(Collectors.toList());
        socProgDAO.insertListAndSetId(socProgDOList);
        return socProgDOList
                .parallelStream()
                .map(socProgDO -> socProgDO.to(SocProgVO.class))
                .collect(Collectors.toList());
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public int update(SocProgUpdateDTO socProgUpdateDTO, TokenUser tokenUser) {
        BigDecimal serviceCost = socProgUpdateDTO.getServiceCost();
        //删除不设置公积金的子项，防止不设置公积金而前端传的零
        List<SocProgItemDTO> progItemList = socProgUpdateDTO.getSocProgItemDTOList();
        if (socProgUpdateDTO.getPayProvidentFund() && !CollectionUtils.isEmpty(progItemList)) {
            progItemList.removeIf(progItem -> 2 == progItem.getItemType());
            socProgUpdateDTO.setSocProgItemDTOList(progItemList);
        }
        if (!CollectionUtils.isEmpty(progItemList)) {
            checkItemName(progItemList, socProgUpdateDTO.getCompanyId(), tokenUser);
        }
        checkName(socProgUpdateDTO.getCompanyId(), socProgUpdateDTO.getProgName(), socProgUpdateDTO.getId(), tokenUser);
        SocProgDO original = selectDOById(socProgUpdateDTO.getId(), tokenUser.getSiteId());
        //转DO
        SocProgDO socProgDO = socProgUpdateDTO.to(SocProgDO.class);
        // 查询最新一个月用这个方案的员工和参保记录
        List<EmpSocInfoDO> filterInfoList = new ArrayList<>();
        EmpSocMonthDO empSocMonth = empSocMonthService
                .checkLastMonthValid(socProgUpdateDTO.getCompanyId(), tokenUser.getSiteId());
        if (!ObjectUtils.isEmpty(empSocMonth)) {
            List<EmpSocInfoDO> empSocInfoList = empSocInfoService
                    .selectNormalInfoByMonthId(empSocMonth.getId(), tokenUser);

            original.setSocAccount(original.getSocAccount() == null ? "" : original.getSocAccount());
            original.setMeAccount(original.getMeAccount() == null ? "" : original.getMeAccount());
            original.setPfAccount(original.getPfAccount() == null ? "" : original.getPfAccount());
            filterInfoList = empSocInfoList.stream()
                    .filter(socInfo -> socProgUpdateDTO.getId().equals(socInfo.getProgId()))
                    .collect(Collectors.toList());
            // 如果帐号不相同再更新社保信息
            if (!original.getSocAccount().equals(socProgDO.getSocAccount()) ||
                    !original.getMeAccount().equals(socProgDO.getMeAccount()) ||
                    !original.getPfAccount().equals(socProgDO.getPfAccount())) {
                // 把用这个方案的人拿出来
                List<String> empIdList = filterInfoList.stream()
                        .map(EmpSocInfoDO::getEmpId).distinct().collect(Collectors.toList());
                if (!CollectionUtils.isEmpty(empIdList)) {
                    Map<String, EmpSocInfoDO> infoMap = filterInfoList.stream()
                            .collect(Collectors.toMap(EmpSocInfoDO::getEmpId, v -> v));
                    List<EmpSocRelatedDO> empSocRelatedList = empSocRelatedService
                            .selectByEmpIdList(empIdList, tokenUser.getSiteId());
                    // 只有增员和正常参保的子项才更换对应账号
                    List<Integer> statusList = Arrays.asList(EmpSocStatus.INCREMENT.getValue(),
                            EmpSocStatus.NORMAL_SOC.getValue());
                    for (EmpSocRelatedDO empSocRelated : empSocRelatedList) {
                        EmpSocInfoDO empSocInfo = infoMap.get(empSocRelated.getEmpId());
                        if (statusList.contains(empSocInfo.getEmpSocStatus())) {
                            empSocRelatedService.setAccountByScheme(SOC_ITEM_TYPE, empSocRelated, socProgDO);
                        }
                        if (statusList.contains(empSocInfo.getEmpMeStatus())) {
                            empSocRelatedService.setAccountByScheme(ME_ITEM_TYPE, empSocRelated, socProgDO);
                        }
                        if (statusList.contains(empSocInfo.getEmpPfStatus())) {
                            empSocRelatedService.setAccountByScheme(PF_ITEM_TYPE, empSocRelated, socProgDO);
                        }
                        empSocRelatedService.updateByIdSelective(empSocRelated, tokenUser.getSiteId(), tokenUser.getUserId());
                    }
                }
            }
        }
        //删除参保方案子项列表
        socProgItemService.deleteBySocProgId(socProgUpdateDTO.getId(), tokenUser);

        if (!CollectionUtils.isEmpty(socProgUpdateDTO.getSocProgItemDTOList())) {
            List<DictItemDO> insertList = new ArrayList<>();
            List<DictItemDO> dictItemList = adminProviderService.selectAllSocItem();
            List<String> itemNameList = dictItemList.stream().map(DictItemDO::getDictItemValue).collect(Collectors.toList());
            // 批量保存参保方案子项
            String socProgId = socProgDO.getId();
            List<SocProgItemDTO> itemList = socProgUpdateDTO.getSocProgItemDTOList();
            List<SocProgItemAddDTO> saveItemList = itemList.stream().map(socProgItemDTO -> {
                SocProgItemAddDTO socProgItemAddDTO = socProgItemDTO.to(SocProgItemAddDTO.class);
                socProgItemAddDTO.setProgId(socProgId);
                if (!itemNameList.contains(socProgItemAddDTO.getItemName())) {
                    DictItemDO insertItem = new DictItemDO();
                    fillDictItem(insertItem, socProgItemAddDTO);
                    insertList.add(insertItem);
                }
                return socProgItemAddDTO;
            }).collect(Collectors.toList());
            socProgItemService.batchSave(saveItemList, tokenUser);
            if (!CollectionUtils.isEmpty(insertList)) {
                adminProviderService.bachSave(insertList);
            }
            //是否更新使用人的参保信息
            if (socProgUpdateDTO.getUpdateItem()) {
                reAmount(filterInfoList, socProgDO.getOnlySocAmount(), saveItemList, serviceCost, tokenUser.getSiteId());
            }
            empSocPlanService.reAmount(socProgUpdateDTO.getId(), socProgDO.getOnlySocAmount(),
                    saveItemList, serviceCost, tokenUser);
        }

        LogKit.logForSave("修改参保方案", "SocProg", socProgUpdateDTO.getCompanyId(), tokenUser);
        //根据主键更新，只更新非null值
        return updateByIdSelective(socProgDO, tokenUser.getSiteId(), tokenUser.getUserId());
    }

    private void reAmount(List<EmpSocInfoDO> filterInfoList, Boolean onlySocAmount,
                          List<SocProgItemAddDTO> schemeItemList, BigDecimal serviceCost, String siteId) {
        if (CollectionUtils.isEmpty(filterInfoList)) {
            return;
        }
        List<String> socInfoIdList = filterInfoList.stream().map(EmpSocInfoDO::getId).collect(Collectors.toList());
        List<EmpSocItemDO> empSocItemList = empSocItemService.selectByEmpSocIdList(socInfoIdList, siteId);
        Map<String, List<EmpSocItemDO>> groupItemMap = empSocItemList.stream()
                .collect(Collectors.groupingBy(EmpSocItemDO::getEmpSocId));
        Map<String, SocProgItemAddDTO> schemeItemMap = schemeItemList.stream()
                .collect(Collectors.toMap(SocProgItemAddDTO::getItemName, v -> v));
        for (EmpSocInfoDO empSocInfo : filterInfoList) {
            List<EmpSocItemDO> empSocInfoItemList = groupItemMap.get(empSocInfo.getId());
            if (CollectionUtils.isEmpty(empSocInfoItemList)) {
                continue;
            }
            BigDecimal personalSocAmount = new BigDecimal("0");
            BigDecimal personalPfAmount = new BigDecimal("0");
            BigDecimal companySocAmount = new BigDecimal("0");
            BigDecimal companyPfAmount = new BigDecimal("0");
            for (EmpSocItemDO empSocItem : empSocInfoItemList) {
                SocProgItemAddDTO schemeItem = schemeItemMap.get(empSocItem.getItemName());
                if (ObjectUtils.isEmpty(schemeItem)) {
                    continue;
                }
                if (onlySocAmount) {
                    empSocItem.setCardinalityDefault(schemeItem.getCardinalityDefaultAmount());
                    empSocItem.setAmountCompany(schemeItem.getAmountCompany());
                    empSocItem.setAmountPersonal(schemeItem.getAmountPersonal());
                    empSocItemService.updateByIdSelective(empSocItem);
                } else {
                    BigDecimal oldAmountCompany = empSocItem.getAmountCompany();
                    BigDecimal oldAmountPersonal = empSocItem.getAmountPersonal();
                    BigDecimal oldCardinalityMin = empSocItem.getCardinalityMin();
                    BigDecimal oldCardinalityMax = empSocItem.getCardinalityMax();
                    BigDecimal cardinalityDefault = empSocItem.getCardinalityDefault();
                    if (empSocItem.getCardinalityDefault().compareTo(schemeItem.getCardinalityMax()) > 0) {
                        cardinalityDefault = schemeItem.getCardinalityMax();
                    }
                    if (empSocItem.getCardinalityDefault().compareTo(schemeItem.getCardinalityMin()) < 0) {
                        cardinalityDefault = schemeItem.getCardinalityMin();
                    }
                    BigDecimal persentCompany = schemeItem.getPersentCompany();
                    BigDecimal persentPersonal = schemeItem.getPersentPersonal();
                    empSocItem.setCardinalityDefault(cardinalityDefault);
                    empSocItem.setCardinalityMax(schemeItem.getCardinalityMax());
                    empSocItem.setCardinalityMin(schemeItem.getCardinalityMin());
                    empSocItem.setMantissaProcCompany(schemeItem.getMantissaProcCompany());
                    empSocItem.setMantissaProcPersonal(schemeItem.getMantissaProcPersonal());
                    empSocItem.setPersentCompany(persentCompany);
                    empSocItem.setPersentPersonal(persentPersonal);
                    // 尾数处理
                    BigDecimal newAmountPersonal = cardinalityDefault
                            .multiply(persentPersonal.multiply(new BigDecimal("0.01")));
                    newAmountPersonal = empSocTransformService
                            .mantissaProcessing(newAmountPersonal, schemeItem.getMantissaProcPersonal());
                    empSocItem.setAmountPersonal(newAmountPersonal);
                    BigDecimal newAmountCompany = cardinalityDefault
                            .multiply(persentCompany.multiply(new BigDecimal("0.01")));
                    newAmountCompany = empSocTransformService
                            .mantissaProcessing(newAmountCompany, schemeItem.getMantissaProcCompany());
                    empSocItem.setAmountCompany(newAmountCompany);
                    // 如果他俩金额不相等再更新
                    boolean amountEqual = oldAmountCompany.compareTo(newAmountCompany) != 0 ||
                            oldAmountPersonal.compareTo(newAmountPersonal) != 0;
                    boolean defaultEqual = oldCardinalityMin.compareTo(schemeItem.getCardinalityMin()) != 0 ||
                            oldCardinalityMax.compareTo(schemeItem.getCardinalityMax()) != 0;
                    if (amountEqual || defaultEqual) {
                        empSocItemService.updateByIdSelective(empSocItem);
                    }
                }
                if (SOC_ITEM_TYPE.equals(empSocItem.getItemType()) || ME_ITEM_TYPE.equals(empSocItem.getItemType())) {
                    personalSocAmount = personalSocAmount.add(empSocItem.getAmountPersonal());
                    companySocAmount = companySocAmount.add(empSocItem.getAmountCompany());
                } else {
                    personalPfAmount = personalPfAmount.add(empSocItem.getAmountPersonal());
                    companyPfAmount = companyPfAmount.add(empSocItem.getAmountCompany());
                }
            }
            if (personalSocAmount.compareTo(empSocInfo.getPersonalSocAmount()) != 0 ||
                    personalPfAmount.compareTo(empSocInfo.getPersonalPfAmount()) != 0 ||
                    companyPfAmount.compareTo(empSocInfo.getCompanyPfAmount()) != 0 ||
                    companySocAmount.compareTo(empSocInfo.getCompanySocAmount()) != 0 ||
                    serviceCost.compareTo(empSocInfo.getServiceCost()) != 0) {
                empSocInfo.setPersonalSocAmount(personalSocAmount);
                empSocInfo.setPersonalPfAmount(personalPfAmount);
                empSocInfo.setCompanySocAmount(companySocAmount);
                empSocInfo.setCompanyPfAmount(companyPfAmount);
                empSocInfo.setServiceCost(serviceCost);
                empSocInfoService.updateByIdSelective(empSocInfo);
            }
        }
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public int deleteByIds(TokenUser tokenUser, String... socProgIds) {
        LogKit.logForSave("删除参保方案", "SocProg", null, tokenUser);
        for (String socProgId : socProgIds) {
            if (empSocInfoService.selectSocProgEmpCount(socProgId) > 0) {
                throw new ParamException("参保方案正在使用");
            }
            if (empSocPlanService.selectEmpCountByScheme(socProgId) > 0) {
                throw new ParamException("增减员计划中还有未反馈的员工正在使用此方案");
            }
        }
        return logicalDeleteByIds(tokenUser.getSiteId(), tokenUser.getUserId(), (Object[]) socProgIds);
    }


    @Override
    public SocProgVO selectById(String socProgId, String siteId) {
        SocProgDO socProgDO = selectDOById(socProgId, siteId);
        if (socProgDO == null) {
            return null;
        }
        return socProgDO.to(SocProgVO.class);
    }

    @Override
    public List<SocProgDO> selectByIds(List<String> socProgIds, TokenUser tokenUser) {
        if (CollectionUtils.isEmpty(socProgIds)) {
            return new ArrayList<>();
        }
        Example example = new Example(SocProgDO.class);
        example.and()
                .andEqualTo("valid", true)
                .andEqualTo("siteId", tokenUser.getSiteId())
                .andIn("id", socProgIds);
        return socProgDAO.selectByExample(example);
    }

    @Override
    public List<SocProgListVO> select(SocProgQuery socProgQuery, String siteId) {
        List<SocProgDO> socProgDOList = selectDO(socProgQuery, siteId);
        if (CollectionUtils.isEmpty(socProgDOList)) {
            return socProgDOList == null ? null : new ArrayList<>();
        }
        return socProgDOList.stream()
                .map(socProgDO -> socProgDO.to(SocProgListVO.class))
                .collect(Collectors.toList());
    }

    @Override
    public int selectCount(SocProgQuery socProgQuery, String siteId) {
        Example example = buildExample(socProgQuery, siteId);
        return socProgDAO.selectCountByExample(example);
    }

    @Override
    public Page<SocProgListVO> selectPage(SocProgQuery socProgQuery, String siteId) {
        Example example = buildExample(socProgQuery, siteId);
        Page<SocProgDO> page = socProgDAO.selectPageByExample(example,
                socProgQuery.getPageNo(),
                socProgQuery.getPageSize());

        return page.to(SocProgListVO.class);
    }

    @Override
    public SocProgDO selectDOById(String socProgId, String siteId) {
        return listById(socProgId, siteId);
    }

    @Override
    public List<SocProgDO> selectDO(SocProgQuery socProgQuery, String siteId) {
        Example example = buildExample(socProgQuery, siteId);
        return socProgDAO.selectByExample(example);
    }

    private void checkName(String companyId, String name, String id, TokenUser tokenUser) {
        Example example = new Example(SocProgDO.class);
        example.and()
                .andEqualTo("siteId", tokenUser.getSiteId())
                .andEqualTo("companyId", companyId)
                .andEqualTo("progName", name)
                .andEqualTo("valid", true)
                .andNotEqualTo("id", id);
        int count = socProgDAO.selectCountByExample(example);
        if (count > 0) {
            throw new HrException("方案名称已存在");
        }
    }

    /**
     * 根据查询参数，构建example
     *
     * @param socProgQuery 查询参数
     * @param siteId       所属站点id
     * @return example
     */
    private Example buildExample(SocProgQuery socProgQuery, String siteId) {
        Example example = new Example(SocProgDO.class);
        example.and()
                .andEqualTo("valid", true)
                .andEqualTo("siteId", siteId);
        if (socProgQuery.getProgName() != null) {
            example.and().andEqualTo("progName", socProgQuery.getProgName());
        }
        if (socProgQuery.getSourceId() != null) {
            example.and().andEqualTo("sourceId", socProgQuery.getSourceId());
        }
        if (socProgQuery.getSocCity() != null) {
            example.and().andEqualTo("socCity", socProgQuery.getSocCity());
        }
        if (socProgQuery.getSocCityName() != null) {
            example.and().andEqualTo("socCityName", socProgQuery.getSocCityName());
        }
        if (socProgQuery.getOnlySocAmount() != null) {
            example.and().andEqualTo("onlySocAmount", socProgQuery.getOnlySocAmount());
        }
        if (socProgQuery.getPayProvidentFund() != null) {
            example.and().andEqualTo("payProvidentFund", socProgQuery.getPayProvidentFund());
        }
        if (socProgQuery.getCompanyId() != null) {
            example.and().andEqualTo("companyId", socProgQuery.getCompanyId());
        }
        // 排序
        ExampleKit.setExampleOrder(example, socProgQuery.getOrders());
        return example;
    }

    @Override
    public Page<SocProgListVO> pageRelation(SocProgQuery socProgQuery, String siteId) {
        PageInfo<SocProgListVO> pageInfo = PageHelper.startPage(socProgQuery.getPageNo(),
                        socProgQuery.getPageSize())
                .doSelectPageInfo(() -> socProgDAO.selectSocProgRelationList(socProgQuery, siteId));
        if (CollectionUtils.isEmpty(pageInfo.getList())) {
            return new Page<>(socProgQuery.getPageSize(), socProgQuery.getPageNo());
        }
        Page<SocProgListVO> socProgListPage = new Page<>(pageInfo.getList(), pageInfo.getPageNum(), pageInfo.getPageSize(), (int) pageInfo.getTotal());
        //此时查询到的社保方案是没有公积金和社保基数的,需要赋值
        for (SocProgListVO socProgListVO : socProgListPage.getEntities()) {

            //查询到这个参保方案的子项
            List<SocProgItemDO> socProgItemDOList = socProgItemService.selectBySocProgId(socProgListVO.getId(), siteId);
            // 固定金额的参保方案,需要拿的基数为CardinalityDefaultAmount字段
            if (socProgListVO.getOnlySocAmount()) {
                //提取出,子项中属于五险或其他险种的,社保基数列表
                List<String> socCardinalityList = socProgItemDOList.stream()
                        .filter(socProgItemDO -> SOC_ITEM_TYPE.equals(socProgItemDO.getItemType()) ||
                                ME_ITEM_TYPE.equals(socProgItemDO.getItemType()))
                        .map(socProgItemDO -> socProgItemDO.getCardinalityDefaultAmount().toString())
                        .collect(Collectors.toList());
                socProgListVO.setSocCardinality(String.join("; ", socCardinalityList));
                //提取出,子项中属于一金或其他公积金,公积金基数列表
                List<String> pfCardinalityList = socProgItemDOList.stream()
                        .filter(socProgItemDO -> PF_ITEM_TYPE.equals(socProgItemDO.getItemType()))
                        .map(socProgItemDO -> socProgItemDO.getCardinalityDefaultAmount().toString())
                        .collect(Collectors.toList());
                socProgListVO.setPfCardinality(String.join("; ", pfCardinalityList));
            } else {
                // 非固定金额的参保方案,需要拿的基数为CardinalityDefault字段
                //提取出,子项中属于五险或其他险种的,社保基数列表
                List<String> socCardinalityList = socProgItemDOList.stream()
                        .filter(socProgItemDO -> SOC_ITEM_TYPE.equals(socProgItemDO.getItemType()) ||
                                ME_ITEM_TYPE.equals(socProgItemDO.getItemType()))
                        .map(socProgItemDO -> socProgItemDO.getCardinalityDefault().toString())
                        .collect(Collectors.toList());
                socProgListVO.setSocCardinality(String.join("; ", socCardinalityList));
                //提取出,子项中属于一金或其他公积金,公积金基数列表
                List<String> pfCardinalityList = socProgItemDOList.stream()
                        .filter(socProgItemDO -> socProgItemDO.getItemType() == 2)
                        .map(socProgItemDO -> socProgItemDO.getCardinalityDefault().toString())
                        .collect(Collectors.toList());
                socProgListVO.setPfCardinality(String.join("; ", pfCardinalityList));
            }
        }
        return socProgListPage;
    }

    @Override
    public List<SocProgListVO> listAllRelation(SocProgQuery socProgQuery, String siteId) {
        return socProgDAO.listAllRelation(socProgQuery, siteId);
    }

    @Override
    public SocProgDO findSocProgByName(String socProgName, String socCityName, String siteId) {

        // 获取方案下的子项列表
        Example example = new Example(SocProgDO.class);
        example.and()
                .andEqualTo("progName", socProgName)
                .andEqualTo("valid", true)
                .andEqualTo("siteId", siteId);

        List<SocProgDO> socProgList = socProgDAO.selectByExample(example);
        if (CollectionUtils.isEmpty(socProgList)) {
            return null;
        }
        if (socProgList.size() > 1) {
            for (SocProgDO socProgDO : socProgList) {
                if (StringUtils.isNotEmpty(socProgDO.getSocCityName()) &&
                        socCityName.equals(socProgDO.getSocCityName())) {
                    return socProgDO;
                }
            }
        }
        return socProgList.get(0);
    }

    @Override
    public List<SocProgDO> selectSocProgByCompanyId(String companyId, TokenUser tokenUser) {
        Example example = new Example(SocProgDO.class);
        example.and()
                .andEqualTo("valid", true)
                .andEqualTo("siteId", tokenUser.getSiteId())
                .andEqualTo("companyId", companyId);
        return socProgDAO.selectByExample(example);
    }

    @Override
    public List<String> getSchemeNameByCompanyId(String companyId, TokenUser tokenUser) {
        List<SocProgDO> socProgList = selectSocProgByCompanyId(companyId, tokenUser);
        if (CollectionUtils.isEmpty(socProgList)) {
            return new ArrayList<>();
        }
        return socProgList.stream().map(SocProgDO::getProgName).collect(Collectors.toList());
    }

    /**
     * 查找公司下同名参保方案
     *
     * @param companyIds 公司Ids
     * @param tokenUser  当前用户
     * @return 公司下同名参保方案
     */
    private List<SocProgDO> selectSocProgByCompanyIdsAndNames(List<String> companyIds, List<String> progNames, TokenUser tokenUser) {
        if (CollectionUtils.isEmpty(companyIds) || CollectionUtils.isEmpty(progNames)) {
            return new ArrayList<>();
        }
        Example example = new Example(SocProgDO.class);
        example.and()
                .andEqualTo("valid", true)
                .andEqualTo("siteId", tokenUser.getSiteId())
                .andIn("companyId", companyIds)
                .andIn("progName", progNames);
        return socProgDAO.selectByExample(example);
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public List<SocProgVO> saveIssuedSocProg(IssuedSocProgAddDTO issuedSocProgAddDTO, TokenUser tokenUser) {
        List<SocProgVO> socProgVOList = new ArrayList<>();
        // 参保方案
        List<SocProgDO> socProgDOList = selectByIds(issuedSocProgAddDTO.getSocProgIds(), tokenUser);
        ParamException.notEmpty(socProgDOList, "没有可以下发的参保方案，请刷新后重试");
        // 方案名称
        List<String> progNames = socProgDOList.parallelStream().map(SocProgDO::getProgName)
                .distinct().collect(Collectors.toList());
        ParamException.isFalse(progNames.size() < socProgDOList.size(), "选择列表中含有同名参保方案，请重新选择");
        List<SocProgDO> progDOList = selectSocProgByCompanyIdsAndNames(issuedSocProgAddDTO.getCompanyIds(), progNames, tokenUser);
        if (!CollectionUtils.isEmpty(progDOList)) {
            Map<String, String> progIdNameMap = progDOList.parallelStream()
                    .collect(Collectors.toMap(BaseDO::getId, SocProgDO::getProgName));
            // 使用模板的用户
            List<EmpSocInfoDO> empSocInfoDOList = empSocInfoService.selectBySocProgIds(new ArrayList<>(progIdNameMap.keySet()));
            if (!CollectionUtils.isEmpty(empSocInfoDOList)) {
                // 按公司Id分组
                Map<String, List<EmpSocInfoDO>> companyIdListMap = empSocInfoDOList
                        .parallelStream().collect(Collectors.groupingBy(EmpSocInfoDO::getCompanyId));
                List<CompanyDO> companyDOList = companyService.selectByIds(new ArrayList<>(companyIdListMap.keySet()), tokenUser.getSiteId());
                Map<String, String> companyIdNameMap = companyDOList.parallelStream()
                        .collect(Collectors.toMap(BaseDO::getId, CompanyDO::getName));
                String errorStr = companyIdListMap.keySet().parallelStream().map(companyId -> {
                    // 错误原因
                    String errorString = "公司：【" + companyIdNameMap.get(companyId) + "】下参保方案：";
                    List<EmpSocInfoDO> empSocInfoList = companyIdListMap.get(companyId);
                    List<String> progIds = empSocInfoList.parallelStream()
                            .map(EmpSocInfoDO::getProgId).distinct().collect(Collectors.toList());
                    errorString += progIds.parallelStream().map(progIdNameMap::get)
                            .collect(Collectors.joining(",", "[", "]"));
                    return errorString;
                }).collect(Collectors.joining("；    "));
                throw new ParamException(errorStr + "正在使用，无法下发");
            }
            deleteByIds(tokenUser, progDOList.parallelStream().map(BaseDO::getId).toArray(String[]::new));
        }
        // 参保方案子项
        List<SocProgItemDO> socProgItemDOList = socProgItemService.selectBySocProgIds(issuedSocProgAddDTO.getSocProgIds(), tokenUser);
        // 根据参保方案分组
        Map<String, List<SocProgItemDO>> progIdListMap = socProgItemDOList.parallelStream()
                .collect(Collectors.groupingBy(SocProgItemDO::getProgId));
        List<SocProgItemAddDTO> socProgItemAddDTOList = new ArrayList<>();

        for (String companyId : issuedSocProgAddDTO.getCompanyIds().parallelStream().distinct().collect(Collectors.toList())) {
            for (SocProgDO socProgDO : socProgDOList) {
                SocProgAddDTO socProgAddDTO = socProgDO.to(SocProgAddDTO.class);
                socProgAddDTO.setIssue(true);
                socProgAddDTO.setSourceId(socProgDO.getId());
                socProgAddDTO.setCompanyId(companyId);
                SocProgVO socProgVO = save(socProgAddDTO, tokenUser);
                socProgVOList.add(socProgVO);
                // 保存参保方案详情
                List<SocProgItemDO> progItemDOList = progIdListMap.get(socProgDO.getId());
                if (CollectionUtils.isEmpty(progItemDOList)) {
                    continue;
                }
                for (SocProgItemDO socProgItemDO : progItemDOList) {
                    SocProgItemAddDTO socProgItemAddDTO = socProgItemDO.to(SocProgItemAddDTO.class);
                    socProgItemAddDTO.setProgId(socProgVO.getId());
                    socProgItemAddDTOList.add(socProgItemAddDTO);
                }
            }
        }
        socProgItemService.batchSave(socProgItemAddDTOList, tokenUser);
        return socProgVOList;
    }

    @Override
    public SocProgVO findSocProgRelation(String progId, TokenUser tokenUser) {
        SocProgDO socProgDO = selectDOById(progId, tokenUser.getSiteId());
        if (socProgDO == null) {
            return null;
        }
        SocProgVO socProgVO = socProgDO.to(SocProgVO.class);
        // 获取方案下的子项列表
        // 所有子项列表
        List<SocProgItemDO> socProgItemDOList = socProgItemService.selectBySocProgId(progId, tokenUser.getSiteId());
        if (CollectionUtils.isEmpty(socProgItemDOList)) {
            return socProgVO;
        }
        // 社保子项列表
        List<SocProgItemListVO> socProgItemSocList = new ArrayList<>();
        // 公积金子项列表
        List<SocProgItemListVO> socProgItemPfList = new ArrayList<>();

        socProgItemDOList.forEach(item -> {
            SocProgItemListVO socProgItemListVO = item.to(SocProgItemListVO.class);
            if (SOC_ITEM_TYPE.equals(item.getItemType()) || ME_ITEM_TYPE.equals(item.getItemType())) {
                socProgItemSocList.add(socProgItemListVO);
            } else if (PF_ITEM_TYPE.equals(item.getItemType())) {
                socProgItemPfList.add(socProgItemListVO);
            }
        });
        socProgVO.setSocProgItemVOSocList(socProgItemSocList);
        socProgVO.setSocProgItemVOPfList(socProgItemPfList);
        //查询是否有人使用过这个参保方案/人次
        Integer integer = socProgDAO.selectCountByProgId(progId);
        socProgVO.setEmpCount(integer);
        return socProgVO;
    }

    @Override
    public List<SocProgDO> selectByIdList(List<String> socProgIdList, TokenUser tokenUser) {
        Example example = new Example(SocProgDO.class);
        example.and()
                .andIn("id", socProgIdList)
                .andEqualTo("valid", true)
                .andEqualTo("siteId", tokenUser.getSiteId());
        return socProgDAO.selectByExample(example);
    }

    @Override
    public SocProgDO selectSocProgByName(String socProgName, String companyId, String siteId) {

        Example example = new Example(SocProgDO.class);
        example.and()
                .andEqualTo("progName", socProgName)
                .andEqualTo("companyId", companyId)
                .andEqualTo("valid", true)
                .andEqualTo("siteId", siteId);

        return socProgDAO.selectOneByExample(example);
    }

    @Override
    public List<String> selectSchemeByEmpSocInfoId(String empSocInfoId, String companyId, TokenUser tokenUser) {
        List<EmpSocItemDO> empSocItemList = empSocItemService.selectByEmpSocId(empSocInfoId, tokenUser.getSiteId());
        List<Integer> itemTypeList = empSocItemList.stream()
                .map(EmpSocItemDO::getItemType).distinct().collect(Collectors.toList());
        // 查询公司下的所有方案
        List<SocProgDO> socProgList = selectByCompanyId(companyId, tokenUser);
        List<String> result = new ArrayList<>();
        for (SocProgDO socProgDO : socProgList) {
            List<SocProgItemDO> socProgItemList = socProgItemService.selectBySocProgId(socProgDO.getId(), tokenUser.getSiteId());
            List<Integer> socProgItemTypeList = socProgItemList.stream()
                    .map(SocProgItemDO::getItemType).distinct().collect(Collectors.toList());
            if (socProgItemTypeList.containsAll(itemTypeList)) {
                result.add(socProgDO.getId());
            }
        }
        return result;
    }

    @Override
    public SocProgVO selectSchemeByEmpRelated(String empId, String companyId, TokenUser tokenUser) {
        EmpSocInfoDO empSocInfo = empSocInfoService.getEmpSocInfoByEmpId(empId, companyId, tokenUser.getSiteId());
        if (ObjectUtils.isEmpty(empSocInfo) || SYNC_DATE.equals(empSocInfo.getProgId())) {
            return null;
        }
        SocProgVO socProgVO = selectById(empSocInfo.getProgId(), tokenUser.getSiteId());
        if (ObjectUtils.isEmpty(socProgVO)) {
            return null;
        }
        List<SocProgItemDO> socProgItemList = socProgItemService
                .selectBySocProgId(socProgVO.getId(), tokenUser.getSiteId());
        // 这里本来有判断 但是判断不用了 懒得和前端说 现在写死全是true了
        socProgVO.setSocRefunds(true);
        socProgVO.setMeRefunds(true);
        socProgVO.setSocRefunds(true);
        socProgVO.setItemList(socProgItemList);
        return socProgVO;
    }

    @Override
    public List<SocProgItemDO> selectItemNameByCompany(String companyId, TokenUser tokenUser) {
        List<SocProgDO> socProgList = selectByCompanyId(companyId, tokenUser);
        if (CollectionUtils.isEmpty(socProgList)) {
            return new ArrayList<>();
        }
        List<String> socProgIdList = socProgList.stream().map(SocProgDO::getId).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(socProgIdList)) {
            return new ArrayList<>();
        }
        return socProgItemService.selectBySocProgIds(socProgIdList, tokenUser);
    }

    @Override
    public Boolean checkEmpItemName(String companyId, String itemName, TokenUser tokenUser) {
        List<String> itemNameList = new ArrayList<>();
        List<SocProgItemDO> socProgItemList = selectItemNameByCompany(companyId, tokenUser);
        if (!CollectionUtils.isEmpty(socProgItemList)) {
            itemNameList = socProgItemList.stream()
                    .map(SocProgItemDO::getItemName).distinct().collect(Collectors.toList());
        }
        return itemNameList.contains(itemName);
    }

    @Override
    public List<SocProgDO> selectByNameList(List<String> nameList, String companyId, String siteId) {
        Example example = new Example(SocProgDO.class);
        example.and()
                .andIn("progName", nameList)
                .andEqualTo("companyId", companyId)
                .andEqualTo("valid", true)
                .andEqualTo("siteId", siteId);
        return socProgDAO.selectByExample(example);
    }

    @Override
    public void updateDict(DictItemDO dictItem, DictItemUpdateDTO update, TokenUser tokenUser) {
        SocProgDO socScheme = new SocProgDO();
        Example example = new Example(SocProgDO.class);
        example.and()
                .andEqualTo("valid", true)
                .andEqualTo("siteId", tokenUser.getSiteId());
        if ("1220".equals(dictItem.getDictCode())) {
            example.and().andEqualTo("socAccount", dictItem.getDictItemValue());
            socScheme.setSocAccount(update.getDictItemValue());
        } else if ("1221".equals(dictItem.getDictCode())) {
            example.and().andEqualTo("meAccount", dictItem.getDictItemValue());
            socScheme.setMeAccount(update.getDictItemValue());
        } else if ("1222".equals(dictItem.getDictCode())) {
            example.and().andEqualTo("pfAccount", dictItem.getDictItemValue());
            socScheme.setPfAccount(update.getDictItemValue());
        } else {
            return;
        }
        socProgDAO.updateByExampleSelective(socScheme, example);
    }

    @Override
    public void deleteDict(DictItemDO dictItem, TokenUser tokenUser) {
        SocProgDO socScheme = new SocProgDO();
        Example example = new Example(SocProgDO.class);
        example.and()
                .andEqualTo("valid", true)
                .andEqualTo("siteId", tokenUser.getSiteId());
        if ("1220".equals(dictItem.getDictCode())) {
            example.and().andEqualTo("socAccount", dictItem.getDictItemValue());
            socScheme.setSocAccount("");
        } else if ("1221".equals(dictItem.getDictCode())) {
            example.and().andEqualTo("meAccount", dictItem.getDictItemValue());
            socScheme.setMeAccount("");
        } else if ("1222".equals(dictItem.getDictCode())) {
            example.and().andEqualTo("pfAccount", dictItem.getDictItemValue());
            socScheme.setPfAccount("");
        } else {
            return;
        }
        socProgDAO.updateByExampleSelective(socScheme, example);
    }

    private List<SocProgDO> selectByCompanyId(String companyId, TokenUser tokenUser) {
        Example example = new Example(SocProgDO.class);
        example.and()
                .andEqualTo("companyId", companyId)
                .andEqualTo("valid", true)
                .andEqualTo("siteId", tokenUser.getSiteId());

        return socProgDAO.selectByExample(example);
    }

    /**
     * 保存或编辑方案时校验方案子项是否与入职模板或员工设置字段相同
     */
    private void checkItemName(List<SocProgItemDTO> itemList, String companyId, TokenUser tokenUser) {
        List<String> itemNameList = itemList.stream()
                .map(SocProgItemDTO::getItemName).collect(Collectors.toList());
        List<String> allEmpFiled = customTemplateFieldService
                .selectAllEmpFiled(companyId, tokenUser);
        for (String itemName : itemNameList) {
            if (allEmpFiled.contains(itemName)) {
                throw new ParamException("社保子项,不可与入职模板或员工设置字段相同");
            }
        }
    }

    private void fillDictItem(DictItemDO insertItem, SocProgItemAddDTO socProgItemAddDTO) {
        insertItem.setSiteId("system");
        insertItem.preInsert("default");
        insertItem.setDictItemKey("10");
        insertItem.setDictItemValue(socProgItemAddDTO.getItemName());
        if (SOC_ITEM_TYPE.equals(socProgItemAddDTO.getItemType())) {
            insertItem.setSubjectType("1");
            insertItem.setDictCode("1213");
        } else if (ME_ITEM_TYPE.equals(socProgItemAddDTO.getItemType())) {
            insertItem.setSubjectType("3");
            insertItem.setDictCode("1214");
        } else {
            insertItem.setSubjectType("2");
            insertItem.setDictCode("1215");
        }
    }
}




